package label

import (
	"fmt"
	"strings"

	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

const (
	// The following labels are recommended by kubernetes https://kubernetes.io/docs/concepts/overview/working-with-objects/common-labels/

	// ManagedByLabelKey is Kubernetes recommended label key, it represents the tool being used to manage the operation of an application
	// For resources managed by TiDB Operator, its value is always phant-operator
	ManagedByLabelKey string = "app.kubernetes.io/managed-by"
	// ComponentLabelKey is Kubernetes recommended label key, it represents the component within the architecture
	ComponentLabelKey string = "app.kubernetes.io/component"
	// NameLabelKey is Kubernetes recommended label key, it represents the name of the application
	NameLabelKey string = "app.kubernetes.io/name"
	// InstanceLabelKey is Kubernetes recommended label key, it represents a unique name identifying the instance of an application
	// It's set by helm when installing a release
	InstanceLabelKey string = "app.kubernetes.io/instance"

	// NamespaceLabelKey is label key used in PV for easy querying
	NamespaceLabelKey string = "app.kubernetes.io/namespace"
	// UsedByLabelKey indicate where it is used. for example, Postrges  and partroni cluster have two services,
	// one for internal component access and the other for end-user
	UsedByLabelKey string = "app.kubernetes.io/used-by"

	// ClusterIDLabelKey is cluster id label key
	ClusterIDLabelKey string = "phant.io/cluster-id"
	// StoreIDLabelKey is store id label key
	StoreIDLabelKey string = "phant.io/store-id"
	// MemberIDLabelKey is member id label key
	MemberIDLabelKey string = "phant.io/member-id"

	// PhantOperator is ManagedByLabelKey label value
	PhantOperator string = "phant-operator"

	// MemberIDLabelKey is member id label key
	PostgresRoleLabelKey string = "phant.io/postgres-role"

	// PatroniRoleLabelKey is postgres role label key
	PatroniRoleLabelKey string = "role"

	// ApplicationLabelKey is App label key
	ApplicationLabelKey string = "app.kubernetes.io/app"

	// AnnStsLastSyncTimestamp is sts annotation key to indicate the last timestamp the operator sync the sts
	AnnStsLastSyncTimestamp = "phant.io/sync-timestamp"

	// PatroniClusterNameLabelKey is Patroni label value
	PatroniClusterNameLabelKey string = "cluster-name"

	// PatroniClusterNameLabelKey is Patroni label value
	PatroniApplicationLabelKey string = "application"

	// PatroniLabelVal is Patroni label value
	PatroniLabelVal string = "partoni"

	// AnnParoniDeleteSlots is annotation key of patroni delete slots.
	AnnParoniDeleteSlots = "patroni.phant.io/delete-slots"

	DeleteSlotsAnn = "delete-slots"
)

// Label is the label field in metadata
type Label map[string]string

func NewOperatorManaged() Label {
	return Label{
		ManagedByLabelKey: PhantOperator,
	}
}

// New initialize a new Label for components of tidb cluster
func New() Label {
	return Label{
		NameLabelKey:      "postgres-cluster",
		ManagedByLabelKey: PhantOperator,
	}
}

// Instance adds instance kv pair to label
func (l Label) Instance(name string) Label {
	l[InstanceLabelKey] = name
	return l
}

// Role adds postgres instance role kv pair to label
func (l Label) Role(role string) Label {
	if role == "" {
		return l
	} else {
		l[PatroniRoleLabelKey] = role
		return l
	}
}

// Patroni assigns Patroni to component key in label
func (l Label) Patroni() Label {
	l[PatroniApplicationLabelKey] = PatroniLabelVal
	return l.Component(PatroniLabelVal)
}

func (l Label) PatroniCluster(name string) Label {
	l[PatroniClusterNameLabelKey] = name
	return l
}

// PatroniApplication assigns Patroni to PatroniApplication key in label
func (l Label) PatroniApplication() Label {
	return Label{
		PatroniApplicationLabelKey: PatroniLabelVal,
		PatroniClusterNameLabelKey: l[PatroniClusterNameLabelKey],
	}
}

// UserBy adds use-by kv pair to label
func (l Label) UsedBy(name string) Label {
	l[UsedByLabelKey] = name
	return l
}

// UsedByPeer adds used-by=peer label
func (l Label) UsedByPeer() Label {
	l[UsedByLabelKey] = "peer"
	return l
}

// UsedByEndUser adds use-by=end-user label
func (l Label) UsedByEndUser() Label {
	l[UsedByLabelKey] = "end-user"
	return l
}

// Namespace adds namespace kv pair to label
func (l Label) Namespace(name string) Label {
	l[NamespaceLabelKey] = name
	return l
}

// Component adds component kv pair to label
func (l Label) Component(name string) Label {
	l[ComponentLabelKey] = name
	return l
}

// Application adds application kv pair to label
func (l Label) Application(name string) Label {
	l[ApplicationLabelKey] = name
	return l
}

// Labels converts label to map[string]string
func (l Label) Labels() map[string]string {
	return l
}

// Copy copy the value of label to avoid pointer copy
func (l Label) Copy() Label {
	copyLabel := make(Label)
	for k, v := range l {
		copyLabel[k] = v
	}
	return copyLabel
}

// LabelSelector gets LabelSelector from label
func (l Label) LabelSelector() *metav1.LabelSelector {
	return &metav1.LabelSelector{MatchLabels: l}
}

// String converts label to a string
func (l Label) String() string {
	var arr []string

	for k, v := range l {
		arr = append(arr, fmt.Sprintf("%s=%s", k, v))
	}

	return strings.Join(arr, ",")
}
